﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="EngineFlowController.cs" company="dimamartovoi">
//   Apache License
//   Version 2.0, January 2004
//   http://www.apache.org/licenses
// </copyright>
// <summary>
//   The engine flow controller.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

using ActorModel.Engine.Core;

namespace ActorModel.Infrastructure
{
    public sealed class EngineFlowController<TState> : IEngineFlowController<TState>
    {
        private readonly AutoResetEvent evoluteWaitHandle = new AutoResetEvent(true);

        private readonly Node<TState>[] nodes;

        private readonly int coresCount = Environment.ProcessorCount;

        private int partialEvolutionCounter;
        private int totalEvolutionsCount;

        public EngineFlowController(IEnumerable<Node<TState>> nodes)
        {
            this.nodes = nodes.ToArray();
            this.Initialize();
        }

        public Node<TState> this[int index]
        {
            get
            {
                return this.nodes[index];
            }
        }

        public void Evolute()
        {
            this.evoluteWaitHandle.WaitOne();

            foreach (var node in this.nodes)
            {
                node.Evolute();
            }

//            this.nodes.AsParallel().ForAll(node => node.Evolute());

//            int rangeSize = this.nodes.Length % coresCount == 0 ? 
//                this.nodes.Length / coresCount :
//                (this.nodes.Length / coresCount) + 1;
//            var partitioner = Partitioner.Create(0, this.nodes.Length, rangeSize);
//            Parallel.ForEach(
//                partitioner,
//                range =>
//                    {
//                        for (int i = range.Item1; i < range.Item2; i++)
//                        {
//                            this.nodes[i].Evolute();
//                        }
//                    });

            this.totalEvolutionsCount++;
        }

        public void SetEvolutionHandler(Func<TState, IEnumerable<TState>, IEnumerable<TState>> evolutionFunc)
        {
            foreach (var node in this.nodes)
            {
                node.EvolutionFunc = evolutionFunc;
            }
        }

        public int TotalEvolutionsCount
        {
            get { return this.totalEvolutionsCount; }
        }

        public override string ToString()
        {
            var builder = new StringBuilder();
            foreach (var node in this.nodes)
            {
                builder.AppendLine(node.ToString());
            }

            return builder.ToString();
        }

        private void Initialize()
        {
            foreach (var node in this.nodes)
            {
                node.NodeStateChanged += this.OnNodeStateChanged;
            }
        }

        private void OnNodeStateChanged(NodeStateChangedEventArgs<TState> nodeStateChangedEventArgs)
        {
            if (Interlocked.Increment(ref this.partialEvolutionCounter) == this.nodes.Length)
            {
                this.partialEvolutionCounter = 0;
                this.evoluteWaitHandle.Set();
            }
        }
    }
}